﻿#region copyright
//<copyright>
// Copyright(C) 2012 TrackerRealm Corporation
// This file is part of the open source project - Jazz. http://jazz.codeplex.com
// 
// Jazzfire is open software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License (AGPL) as published by the Free Software Foundation, version 3 of the License.
// 
// Jazzfire is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License (AGPL) for more details.
// 
// You should have received a copy of the GNU General Public License along with Jetfire.  If not, see <http://www.gnu.org/licenses/>.
//
// REMOVAL OF THIS NOTICE IS VIOLATION OF THE COPYRIGHT. 
//</copyright>
#endregion
# region using
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using TrackerRealm.Jazz.Client.Custom;
using TrackerRealm.Jazz.Client;
using TrackerRealm.Jazz.Common;
using System.Reflection;
# endregion

namespace TrackerRealm.Jazz.WindowsForm
{
    public partial class FormJazz : Form
    {
        # region Constructor
        ClientNexus nexus = null;
        jObject obj = null;

        public FormJazz()
        {
            InitializeComponent();
            this.nexus = ClientNexus.Create();
            this.CreateObjects();
            this.labelLoggedIn.Text = "Logged-in user: " + this.nexus.LoginProfile.Name;
            this.ShowNavigation();
        }
        # endregion
        # region Navigation
        private void ShowNavigation()
        {
            this.helpToolStripMenuItem.Click += new EventHandler(helpToolStripMenuItem_Click);
            this.treeViewNav.NodeMouseClick += new TreeNodeMouseClickEventHandler(treeViewNav_NodeMouseClick);
            // Bind users to Login menu item
            foreach (jUser user in this.nexus.Cache.OfType<jUser>().Where(u => !u.IsEmpty))
            {
                string roles = user.Profile.Roles.ToReadableString("Name");
                roles = roles == String.Empty ? String.Empty : String.Format(" [Roles: {0}]", roles);
                ToolStripItem item = new ToolStripButton(String.Format("{0}{1}", user.Name, roles));
                item.Width = 180;
                item.Tag = user;
                item.Click += new EventHandler(loginUser_Click);
                this.loginToolStripMenuItem.DropDownItems.Add(item);
            }
            this.RefreshNodes();
        }
        void RefreshNodes()
        {
            this.newToolStripMenuItem.DropDownItems.Clear();
            this.treeViewNav.Nodes.Clear();
            // Add classes that user is allowed to create
            Creator creator = new Creator(this.nexus);
            IEnumerable<jMethodInfo> methods = creator.ClassMethods();
            foreach (jMethodInfo mi in methods)
            {
                if (!mi.IsExecutable)
                    continue;
                ToolStripItem tsi = new ToolStripButton(mi.Name);
                tsi.Width = 120;
                tsi.Tag = mi;
                tsi.Click += new EventHandler(tsiNewObject_Click);
                this.newToolStripMenuItem.DropDownItems.Add(tsi);
                if (mi.Name.Contains("Role"))
                {
                    // Add Roles to TreeView
                    TreeNode rolesNode = new TreeNode("Roles");
                    this.treeViewNav.Nodes.Add(rolesNode);
                    IEnumerable<jRole> roles = nexus.Cache.OfType<jRole>().Where(w => !w.IsEmpty).OrderBy(r => r.Name);
                    rolesNode.Nodes.AddRange(roles.Select(r => new TreeNode(r.Name) { Tag = r }).ToArray());
                }
                else if (mi.Name.Contains("User"))
                {
                    // Add Users to TreeView
                    TreeNode usersNode = new TreeNode("Users");
                    this.treeViewNav.Nodes.Add(usersNode);
                    IEnumerable<jUser> users = nexus.Cache.OfType<jUser>().Where(w => !w.IsEmpty).OrderBy(r => r.Name);
                    usersNode.Nodes.AddRange(users.Select(r => new TreeNode(r.Name) { Tag = r }).ToArray());
                }
                else if (mi.Name.Contains("To_Do"))
                {
                    // Add To Do Items to TreeView
                    TreeNode itemsNode = new TreeNode("To Do Items");
                    this.treeViewNav.Nodes.Add(itemsNode);
                    IEnumerable<GroupToDo> items = nexus.Cache.OfType<GroupToDo>().Where(w => !w.IsEmpty && w.IsAccessible).OrderBy(r => r.Name);
                    // Display the CurrentState and the Name in the text of the node
                    // Save the item as the Tag in the node, so that it can be retrieved when clicked
                    itemsNode.Nodes.AddRange(items.Select(r => new TreeNode("[" + r.CurrentState.Name + "] " + r.Name) { Tag = r }).ToArray());
                }
            }
            this.newToolStripMenuItem.Visible = this.newToolStripMenuItem.DropDownItems.Count > 0;
            this.treeViewNav.ExpandAll();
            // Highlight selected node
            this.HighlightSelectedNode();
        }
        void HighlightSelectedNode()
        {
            foreach (TreeNode tn in this.treeViewNav.Nodes)
            {
                tn.BackColor = Color.Transparent;
                foreach (TreeNode node in tn.Nodes)
                {
                    if (node.Tag == this.obj)
                        node.BackColor = Color.LightGreen;
                    else
                        node.BackColor = Color.Transparent;
                }
            }
        }
        # endregion
        # region DisplayObject Form
        void DisplayObject(jObject obj)
        {
            if (obj == null)
                return;
            if (!obj.IsAccessible)
            {
                this.panelMembers.Controls.Clear();
                // Tell logged in user that the displayed object is no longer accessible.
                MessageBox.Show(obj.ID + " is no longer available");
                return;
            }
            this.AddHeading(obj);
            this.panelMembers.Controls.Clear();
            // Display core properties
            IEnumerable<jPropertyInfo> properties = obj.GetWrappedMembers().OfType<jPropertyInfo>().ToList();
            // Display Properties in this class
            jPropertyInfo piWs = properties.SingleOrDefault(p => p.Name == "Workspace");
            jPropertyInfo piState = properties.SingleOrDefault(p => p.Name == "CurrentState");
            jPropertyInfo pi = null;
            // Display the name property for jRole and jUser
            if (obj.GetType().IsSubclassOf(typeof(jNameUnique)))
            {
                pi = properties.SingleOrDefault(p => p.Name == "Name");
                this.AddReadOnlyLabel(obj, pi);
            }
            // Get the properties that are in the subclass GroupToDo
            properties = properties.Where(p => p.Member.DeclaringType == typeof(GroupToDo));
            bool showSaveButton = false;
            // Display every property in the subclass (except ClientNexus)
            foreach (jPropertyInfo pi1 in properties.Reverse())
            {
                if (pi1.Name == "ClientNexus")
                    continue;
                pi = properties.SingleOrDefault(p => p.Name == pi1.Name);
                if (pi.CanWrite)
                {
                    this.AddWritableControl(obj, pi);
                    showSaveButton = true;
                }
                else if (pi.CanRead)
                    this.AddReadOnlyLabel(obj, pi);
            }
            // Add the Workspace and CurrentState properties
            this.AddLabel(piWs.Name, obj.Workspace.Name);
            this.AddLabel(piState.Name, obj.CurrentState.Name);
            // Display Commands
            this.AddCommands(obj, showSaveButton, true);
        }
        # endregion
        # region DisplaySchema Form
        void DisplaySchema(jObject obj)
        {
            if (obj == null)
                return;
            if (!obj.IsAccessible)
            {
                this.panelMembers.Controls.Clear();
                MessageBox.Show(obj.ID + " is no longer available");
                return;
            }
            this.AddHeading(obj);
            this.panelMembers.Controls.Clear();
            SplitContainer splitter = new SplitContainer();
            splitter.Dock = DockStyle.Fill;
            splitter.Orientation = Orientation.Horizontal;
            this.panelMembers.Controls.Add(splitter);
            // Add DataGridView to display Properties
            DataGridView view = new DataGridView();
            view.Dock = DockStyle.Fill;
            // Fixed Size Grid with no editing
            view.AllowUserToAddRows = false;
            view.EditMode = DataGridViewEditMode.EditProgrammatically;
            view.ColumnAdded += new DataGridViewColumnEventHandler(viewProperties_ColumnAdded);
            view.CellFormatting += new DataGridViewCellFormattingEventHandler(viewProperties_CellFormatting);
            view.DataError += new DataGridViewDataErrorEventHandler(view_DataError);
            BindingSource source = new BindingSource();
            view.DataSource = source;
            // Display subClass properties
            IEnumerable<jPropertyInfo> properties = obj.ClassProperties().ToList();
            foreach (jPropertyInfo pi1 in properties)
            {
                if (pi1.Name == "ClientNexus")
                    continue;
                source.Add(new NVRW(pi1, obj));
            }
            // Display Core Properties
            IEnumerable<jPropertyInfo> members = obj.GetWrappedMembers().OfType<jPropertyInfo>().ToList();
            string[] extras = new string[] { "ID", "Name", "Class", "CurrentState", "Status", "IsDeleted", "IsValid", "Workspace" };
            foreach (string extra in extras)
            {
                jPropertyInfo pi = members.SingleOrDefault(p => p.Name == extra);
                source.Add(new NVRW(pi, obj));
            }
            splitter.Panel1.Controls.Add(view);
            // Add DataGridView to display Methods
            view = new DataGridView();
            view.Dock = DockStyle.Fill;
            view.AllowUserToAddRows = false;
            view.EditMode = DataGridViewEditMode.EditProgrammatically;
            view.ColumnAdded += new DataGridViewColumnEventHandler(viewMethods_ColumnAdded);
            view.CellFormatting += new DataGridViewCellFormattingEventHandler(viewMethods_CellFormatting);
            view.DataError += new DataGridViewDataErrorEventHandler(view_DataError);
            source = new BindingSource();
            view.DataSource = source;
            // Display the methods in the subClass
            IEnumerable<jMethodInfo> methods = obj.ClassMethods().ToList();
            foreach (jMethodInfo mi in methods)
            {
                source.Add(new NVRW(mi));
            }
            splitter.Panel2.Controls.Add(view);
            // Display Commands for the schema ("Show Form")
            this.AddCommands(obj, false, false);
        }
        # endregion

        # region Controls
        void AddWritableControl(jObject obj, jPropertyInfo pi)
        {
            Type t = pi.PropertyInfo.PropertyType;
            switch (t.Name)
            {
                case "DateTime":
                    this.AddDateTimePicker(obj, pi);
                    return;
                case "TimeSpan":
                    this.AddLabel(pi.Name, pi.Read(this.obj).ToString());
                    return;
                case "String":
                    this.AddTextBox(obj, pi);
                    return;
                case "Profile":
                    this.AddDropDownList(obj, pi);
                    return;
                default:
                    if (t.IsSubclassOf(typeof(jObject)))
                    {
                        this.AddDropDownList(obj, pi);
                        return;
                    }
                    this.AddLabel(pi.Name, pi.Read(this.obj).ToString());
                    return;
            }
        }
        void AddHeading(jObject obj)
        {
            string name = String.Empty;
            if (obj is jNameUnique)
                name = obj.Name;
            this.labelTitle.Text = String.Format("{0}: {1}", obj.Class.Name, name);
        }
        void AddCommands(jObject obj, bool showSaveButton, bool details)
        {
            Panel pnl = new Panel();
            ToolTip tt = new ToolTip();
            pnl.Dock = System.Windows.Forms.DockStyle.Top;
            pnl.Size = new System.Drawing.Size(100, 26);
            pnl.Name = "panelCommands";
            this.panelMembers.Controls.Add(pnl);
            Button btnSchema = this.AddButton(pnl, "Schema");
            btnSchema.Dock = DockStyle.Right;
            if (details)
            {   // Form is displayeed
                tt.SetToolTip(btnSchema, "Show the schema of the jObject");
                btnSchema.Click += new EventHandler(btnSchema_Click);
                if (showSaveButton)
                {
                    Button btnSave = this.AddButton(pnl, "Save");
                    tt.SetToolTip(btnSave, "Save any changes made to the form");
                    btnSave.Click += new EventHandler(btnSave_Click);
                }
                // add commands found in class
                IEnumerable<jMethodInfo> methods = obj.ClassMethods();
                foreach (jMethodInfo mi in methods)
                {
                    if (!mi.IsExecutable)
                        continue;
                    if (mi.MethodInfo.GetParameters().Length != 0)
                        continue;
                    Button btnCommand = this.AddButton(pnl, mi.Name);
                    tt.SetToolTip(btnCommand, "Execute the command: " + mi.Name);
                    btnCommand.Click += new EventHandler(btnCommand_Click);
                }
            }
            else
            {   // Schema is displayed
                btnSchema.Text = "Form";
                btnSchema.Click += new EventHandler(btnForm_Click);
            }
        }
        Button AddButton(Control pnl, string name)
        {
            Button btnCommand = new Button();
            btnCommand.Name = "btnCommand" + name;
            btnCommand.Dock = DockStyle.Left;
            btnCommand.AutoSize = true;
            btnCommand.Text = name;
            btnCommand.UseVisualStyleBackColor = true;
            pnl.Controls.Add(btnCommand);
            btnCommand.Tag = name;
            return btnCommand;
        }
        void AddReadOnlyLabel(jObject obj, jPropertyInfo pi)
        {
            if (pi == null)
                return;
            GroupBox gBox = new GroupBox();
            gBox.Dock = DockStyle.Top;
            gBox.Size = new Size(400, 36);
            this.panelMembers.Controls.Add(gBox);
            gBox.Text = pi.Name;
            Label lbl = new Label();
            lbl.BackColor = Color.White;
            lbl.AutoSize = true;
            lbl.Dock = DockStyle.Fill;
            lbl.Name = "lb" + pi.Name;
            lbl.Text = obj.ReadString(pi.Name);
            gBox.Controls.Add(lbl);
        }
        void AddLabel(string name, string value)
        {
            GroupBox gBox = new GroupBox();
            gBox.Dock = DockStyle.Top;
            gBox.Size = new Size(400, 36);
            this.panelMembers.Controls.Add(gBox);
            gBox.Text = name;
            Label lbl = new Label();
            lbl.BackColor = Color.White;
            lbl.AutoSize = true;
            lbl.Dock = DockStyle.Fill;
            lbl.Name = "lb" + name;
            lbl.Text = value;
            gBox.Controls.Add(lbl);
        }
        void AddTextBox(jObject obj, jPropertyInfo pi)
        {
            GroupBox gBox = new GroupBox();
            gBox.Dock = DockStyle.Top;
            gBox.Size = new Size(400, 36);
            this.panelMembers.Controls.Add(gBox);
            gBox.Text = pi.Name;
            TextBox lbl = new TextBox();
            lbl.BackColor = Color.White;
            lbl.Tag = pi;
            lbl.AutoSize = true;
            lbl.Dock = DockStyle.Fill;
            lbl.Name = "tb" + pi.Name;
            lbl.Text = pi.Read(obj).ToString();
            gBox.Controls.Add(lbl);
        }
        void AddDateTimePicker(jObject obj, jPropertyInfo pi)
        {
            GroupBox gBox = new GroupBox();
            gBox.Dock = DockStyle.Top;
            gBox.Size = new Size(400, 36);
            this.panelMembers.Controls.Add(gBox);
            gBox.Text = pi.Name;
            DateTimePicker dtp = new DateTimePicker();
            dtp.BackColor = Color.White;
            dtp.Tag = pi;
            dtp.Dock = DockStyle.Left;
            dtp.Format = DateTimePickerFormat.Time;
            dtp.ShowUpDown = true;
            dtp.AutoSize = true;
            dtp.Name = "dtp" + pi.Name;
            dtp.Value = (DateTime) pi.Read(obj);
            gBox.Controls.Add(dtp);
            dtp = new DateTimePicker();
            dtp.Tag = pi;
            dtp.Dock = DockStyle.Left;
            dtp.AutoSize = true;
            dtp.Name = "dtp" + pi.Name;
            dtp.Value = (DateTime)pi.Read(obj);
            gBox.Controls.Add(dtp);
        }
        void AddDropDownList(jObject obj, jPropertyInfo pi)
        {
            GroupBox gBox = new GroupBox();
            gBox.Dock = DockStyle.Top;
            gBox.Size = new Size(400, 36);
            this.panelMembers.Controls.Add(gBox);
            gBox.Text = pi.Name;
            ComboBox cb = new ComboBox();
            cb.BackColor = Color.White;
            cb.Tag = pi;
            cb.AutoSize = true;
            cb.Dock = DockStyle.Fill;
            cb.Name = "dtp" + pi.Name;
            // Fill the combox box
            IEnumerable<jObject> objects = this.nexus.Cache.OfType<jObject>().Where(m => m.Class.Name == pi.PropertyInfo.PropertyType.Name);
            cb.DisplayMember = "Subject";
            object o = pi.Read(obj);
            if (o is jNameUnique)
                cb.DisplayMember = "Name";
            cb.ValueMember = "ID";
            cb.Items.AddRange(objects.ToArray());
            cb.SelectedItem = o;
            gBox.Controls.Add(cb);
        }
        # endregion
        # region DataGridView Events for Schema Form
        void view_DataError(object sender, DataGridViewDataErrorEventArgs e)
        {
            string s = e.ToString();
        }
        void viewProperties_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            if (e.ColumnIndex == 2 || e.ColumnIndex == 3)
            {
                if (e.Value != null && e.Value.ToString() == "True")
                    e.CellStyle = new DataGridViewCellStyle() { BackColor = Color.Green, ForeColor = Color.Green, Alignment = DataGridViewContentAlignment.MiddleCenter };
            }
        }
        void viewProperties_ColumnAdded(object sender, DataGridViewColumnEventArgs e)
        {
            if (e.Column.Index == 0)
                e.Column.HeaderText = "Property Name";
            if (e.Column.Index == 1)
                e.Column.Width = 240;
            else if (e.Column.Index == 2 | e.Column.Index == 3)
            {
                if (e.Column.Index == 2)
                    e.Column.HeaderText = "Can Read";
                else
                    e.Column.HeaderText = "Can Write";
                e.Column.Width = 70;
                e.Column.DataGridView.Columns[e.Column.Index].DefaultCellStyle = new DataGridViewCellStyle() { BackColor = Color.Red, ForeColor = Color.Red, Alignment = DataGridViewContentAlignment.MiddleCenter };
            }
        }
        void viewMethods_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e)
        {
            if (e.ColumnIndex == 3)
            {
                if (e.Value != null && e.Value.ToString() == "True")
                    e.CellStyle = new DataGridViewCellStyle() { BackColor = Color.Green, ForeColor = Color.Green, Alignment = DataGridViewContentAlignment.MiddleCenter };
            }
        }
        void viewMethods_ColumnAdded(object sender, DataGridViewColumnEventArgs e)
        {
            if (e.Column.Index == 0)
                e.Column.HeaderText = "Method Name";
            else if (e.Column.Index == 1)
                e.Column.Visible = false;
            else if (e.Column.Index == 2)
                e.Column.Visible = false;
            else if (e.Column.Index == 3)
            {
                e.Column.HeaderText = "Is Executable";
                e.Column.Width = 120;
                e.Column.DataGridView.Columns[e.Column.Index].DefaultCellStyle = new DataGridViewCellStyle() { BackColor = Color.Red, ForeColor = Color.Red, Alignment = DataGridViewContentAlignment.MiddleCenter };
            }
        }
        # endregion
        # region Event Handlers for jObject form
        void btnSave_Click(object sender, EventArgs e)
        {
            try
            {
                this.Save(false);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        private void Save(bool confirmChange)
        {
            if (this.obj == null)
                return;
            if (!(obj is GroupToDo))
                return;
            string message = String.Empty;
            foreach (Control control in this.panelMembers.Controls)
            {
                GroupBox gBox = control as GroupBox;
                if (gBox == null)
                    continue;
                foreach (Control c in gBox.Controls)
                {
                    jPropertyInfo pi = c.Tag as jPropertyInfo;
                    if (c is TextBox)
                    {
                        TextBox tb = c as TextBox;
                        string s = pi.Read(this.obj).ToString();
                        if (s != tb.Text)
                        {   // Show user the change
                            message += String.Format("{0} changed from {1} to {2}{3}", pi.Name, s, tb.Text, Environment.NewLine);
                            if (confirmChange)
                            {
                                DialogResult result = MessageBox.Show(message + " Do you wish to save these changes?", "Save Changes?", MessageBoxButtons.YesNo);
                                if (result != DialogResult.Yes)
                                    return;
                            }
                            pi.Write(this.obj, tb.Text);
                        }
                    }
                }
            }
            if (message == String.Empty)
                message = "Nothing changed";
            else
            {
                this.DisplayObject(this.obj);
                this.RefreshNodes();
            }
            if (!confirmChange)
                MessageBox.Show(message);
        }
        void btnSchema_Click(object sender, EventArgs e)
        {
            if (this.obj == null)
                return;
            this.DisplaySchema(this.obj);
        }
        void btnForm_Click(object sender, EventArgs e)
        {
            if (this.obj == null)
                return;
            this.DisplayObject(this.obj);
        }
        void btnCommand_Click(object sender, EventArgs e)
        {
            try
            {
                if (this.obj == null)
                    return;
                Button btn = sender as Button;
                string command = btn.Tag.ToString();
                this.ExecuteCommand(command);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        private void ExecuteCommand(string command)
        {
            jMethodInfo mi = obj.GetWrappedMembers().OfType<jMethodInfo>().FirstOrDefault(m => m.Name == command);
            if (mi == null)
                MessageBox.Show("Cannot find " + command);
            else
            {
                this.Save(true);
                mi.Execute(this.obj);
                this.DisplayObject(this.obj);
                this.RefreshNodes();
            }
        }
        # endregion
        # region Event Handlers for treeview
        void treeViewNav_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            jObject tag = e.Node.Tag as jObject;
            if (tag == null)
                return;
            this.obj = tag;
            // display the jObject
            this.DisplayObject(tag);
            this.HighlightSelectedNode();
        }
        # endregion
        # region Event Handlers for menu
        void loginUser_Click(object sender, EventArgs e)
        {
            ToolStripItem item = sender as ToolStripItem;
            jUser user = item.Tag as jUser;
            this.nexus.ChangeLoginProfile(user.Profile);
            this.labelLoggedIn.Text = "Logged-in user: " + this.nexus.LoginProfile.Name;
            this.RefreshNodes();
            this.DisplayObject(this.obj);
        }
        void helpToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FormHelp help = new FormHelp();
            help.ShowDialog();
        }
        void tsiNewObject_Click(object sender, EventArgs e)
        {
            ToolStripItem item = sender as ToolStripItem;
            if (item == null)
                return;
            jMethodInfo mi = item.Tag as jMethodInfo;
            if (mi == null)
                return;
            FormNamePrompt prompt = new FormNamePrompt(this.nexus, mi);
            prompt.ShowDialog();
            this.RefreshNodes();
        }
        # endregion

        # region Initialization of data
        /// <summary>
        /// Create a bunch of objects - useful for testing or demo sites
        /// </summary>
        /// <param name="nexus"></param>
        void CreateObjects()
        {
            // Create Users
            IEnumerable<jRole> workflows = nexus.Cache.OfType<jRole>().Where(w => !w.IsEmpty);
            if (workflows.Count() == 0)
            {
                // ToDo Role allows users to read/write To Do Items.
                jRole roleToDo = jRole.Create(nexus, "ToDo");
                jRole roleManager = jRole.Create(nexus, "Manager");
                // Add users
                jUser george = jUser.Create(nexus, "George");
                george.Profile.AddRole(roleToDo);
                // Log in George
                this.nexus.ChangeLoginProfile(george.Profile);
                jUser sarah = jUser.Create(nexus, "Sarah");
                sarah.Profile.AddRole(roleToDo);
                jUser.Create(nexus, "Admin", "UserAdmin");
                List<jRole> list = new List<jRole>();
                list.Add(roleManager);
                list.Add(roleToDo);
                jUser.Create(nexus, "Manager", list);
                // Create new Workspace for To Do that only users with the ToDo role can access
                jWorkspace ws = jWorkspace.Create(nexus, "To Do Workspace");
                ws.Add(jAclItem.Accessible(roleToDo));
                // Create To DoItem by George (The GroupToDo class moves the object into the 'To Do Workspace'
                GroupToDo item = new GroupToDo(this.nexus, "To Do Item for George");
                // login as Sarah, so that the next item is created by Sarah
                this.nexus.ChangeLoginProfile(sarah);
                item = new GroupToDo(this.nexus, "To Do Item for Sarah");
                // login as George, to match the demo script
                this.nexus.ChangeLoginProfile(george);
            }
        }
        # endregion
    }
    # region Name/Value/Read/Write helper class for schema display
    /// <summary>
    /// Helper class used for schema display - Name Value Read Write
    /// </summary>
    public class NVRW
    {
        public NVRW()
        {
        }
        public NVRW(jPropertyInfo pi, jObject obj)
        {
            this.Name = pi.Name;
            this.Value = obj.ReadString(pi.Name);
            this.CanRead = pi.CanRead;
            this.CanWrite = pi.CanWrite;
            this.InitializeGrants(pi.PropertyInfo);
            if (pi.WriteAccessor.MethodInfo != null)
                this.InitializeGrants(pi.WriteAccessor.MethodInfo);
        }
        public NVRW(jMethodInfo mi)
        {
            this.Name = mi.Name;
            this.Value = String.Empty;
            this.CanRead = false;
            this.CanWrite = mi.IsPublic;
            this.InitializeGrants(mi.MethodInfo);
        }
        /// <summary>
        /// This method finds the Grant Constructs and save them in the corresponding Grants
        /// </summary>
        /// <param name="member">MemberInfo</param>
        private void InitializeGrants(MemberInfo member)
        {
            object[] stateMethods = member.GetCustomAttributes(typeof(GrantStatesAttribute), true);
            object[] roleMethods = member.GetCustomAttributes(typeof(GrantRolesAttribute), true);
            object[] propertyAttributes = member.GetCustomAttributes(typeof(GrantPropertyAttribute), true);
            string setter = String.Empty;
            if ((member.Name.StartsWith("set_")))
                setter = "[Setter] ";
            foreach (GrantStatesAttribute attr in stateMethods)
            {
                this.GrantStates = setter + String.Join("; ", attr.States.Select(s => s));
            }
            foreach (GrantRolesAttribute attr in roleMethods)
            {
                this.GrantRoles = setter + String.Join("; ", attr.Roles.Select(s => s));
            }
            string grants = String.Join("; ", propertyAttributes.Select(s => ((GrantPropertyAttribute)s).Property));
            if (grants != String.Empty)
                this.GrantProperties = setter + grants;
        }
        /// <summary>
        /// Gets and Sets the Name
        /// </summary>
        public string Name
        {
            get;
            set;
        }
        /// <summary>
        /// Gets and Sets the Value
        /// </summary>
        public string Value
        {
            get;
            set;
        }
        /// <summary>
        /// Gets and Sets the CanRead flag
        /// </summary>
        public bool CanRead
        {
            get;
            set;
        }
        /// <summary>
        /// Gets and Sets the CanWrite flag - doubles as the IsExecutable flag for Methods
        /// </summary>
        public bool CanWrite
        {
            get;
            set;
        }
        /// <summary>
        /// Gets and Set the Grant States
        /// </summary>
        public string GrantStates
        {
            get;
            set;
        }
        /// <summary>
        /// Gets and Set the Grant Roles
        /// </summary>
        public string GrantRoles
        {
            get;
            set;
        }
        /// <summary>
        /// Gets and Set the Grant Properties
        /// </summary>
        public string GrantProperties
        {
            get;
            set;
        }
    }
    # endregion
}